// Copyright 2008 Jamethiel Wickentower (David Lloyd)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

integer CHANNEL = 999;
integer D_CHANNEL = 0;
integer MEMORY_LENGTH = 25;

key gOwner;
string gOwnerName;

vector gDestination = <-1, -1, -1>;

list gAllowed;

list gCommands;

// Clean Message
//
// Makes the message lower case and strips leading and trailing white space.
string clean_message(string message) {
    return(llToUpper(llStringTrim(message, STRING_TRIM)));
}

// Discover Owner
//
// Gets detail about the item owner, such as KEY and NAME.
discover_owner() {
    gOwner = llGetOwner();
    gOwnerName = llKey2Name(gOwner);
    llOwnerSay("Rezzed for " + gOwnerName);
}

// Setup the Public Listener
//
// This sets up the public listener.
setup_public_listener()
{
    CHANNEL = llCeil(llFrand(99900)) + 100;
    llOwnerSay("Listening on channel " + (string)CHANNEL);
    llListen(CHANNEL, "", gOwner, "");
}

// Make Commands
//
// This simply creates what directions we understand.
make_commands() {
    gCommands = llListSort([ "DESTINATION", "HELP", "ADD", "REMOVE", "WHO", "ALLOWED" ], 1, TRUE);
}

// Handle Public Message
//
// This handles public messages. This is a bit ham-fisted at the moment as it should
// start some type of IM with the avatar or open up a random chat channel. That will
// be implemented later.
handle_public_message(string message, string name, key id) {
    string theMessage = clean_message(message);
    
    // Here we start parsing commands that may have more than one word
    list parse_it = llParseString2List(message, [" ", ","], []);
    integer len = llGetListLength(parse_it);
    string command = clean_message(llList2String(parse_it, 0));
    
    if (command == "DESTINATION" && len == 1) {
        llInstantMessage(id, "My current destination is " + (string)gDestination + ".");
        return;
    }
    
    if (command == "WHO" || command == "ALLOWED") {
        string message = "The following are currently allowed: \n";
        
        integer j;
        integer a_len = llGetListLength(gAllowed);
        for (j = 0; j < a_len; j++) {
            key k = (key)llList2String(gAllowed, j);
            string r_name = llKey2Name(k);
            
            message += name + " => " + (string)k + "\n";
        }
        
        llInstantMessage(id, message);
        return;
    }
    
    if (command == "DESTINATION" && len == 4) {
        float x = (float)llList2String(parse_it, 1);
        float y = (float)llList2String(parse_it, 2);
        float z = (float)llList2String(parse_it, 3);
        
        gDestination = < x, y, z >;
        
        llInstantMessage(id, "Set new destination to " + (string)gDestination 
            + ".");
        
        return;
    }
    
    list rest = llDeleteSubList(parse_it, 0, 0);
    if (command == "ADD") {
        string who = llDumpList2String(rest, " ");
        if (who != "") {
            gAllowed += who;
            llOwnerSay("Added '" + who + "'.");
        }
        return;
    }
    
    if (command == "REMOVE") {
        string who = llDumpList2String(rest, " ");
        if (who != "") {
            integer pos = llListFindList(gAllowed, [ who ]);
            if (pos != -1) {
                gAllowed = llDeleteSubList(gAllowed, pos, pos);
                llOwnerSay("Removed '" + who + "'.");
            } else {
                llOwnerSay("'" + who + "' isn't on the allowed list anyway.");
            }
        }
        return;
    }
}

// Configure Sit
//
// Configures an avatars sit setting
configure_sit() {
    llSitTarget(< 0, 0, 0.5>, ZERO_ROTATION);
    llSetSitText("Teleport");
}

// Move To
//
// Quickly moves the avatar to the desired location.
move_to(vector src, vector dest) {
    float dist = llVecDist(src, dest);
    integer passes = llCeil(llLog(dist/10.0) / llLog(2.0));
    integer i;
    list params = [ PRIM_POSITION, dest ];
    for (i = 0; i < passes; i++) {
        params += params;
    }
    llSetPrimitiveParams(params);
}

// Teleport
//
// This funcction will actually perform the teleport.
teleport(key id) {
    if (gDestination == < -1, -1, -1 >) {
        llWhisper(0, "I am not yet configured - please contact my owner.");
        return;
    }
    
    vector origin = llGetPos();
    move_to(origin, gDestination);
    llUnSit(id);
    move_to(gDestination, origin);
}

default
{
    state_entry()
    {
        discover_owner();
        make_commands();
        setup_public_listener();
        configure_sit();
    }
    
    listen(integer channel, string name, key id, string message) {
        if (channel == CHANNEL  && id == gOwner) {
             handle_public_message(message, name, id);
             return;
        }
    }
    
    touch_start(integer total_number) {
        if (llDetectedKey(0) == gOwner) {
            llOwnerSay("Listening on channel " + (string)CHANNEL);
        } 
    }
 
    on_rez(integer start_param) {
        llResetScript();
    }
               
    changed(integer changedBits) {
        if (changedBits & CHANGED_LINK) {
            key id = llAvatarOnSitTarget();
            string av_name = llKey2Name(id);
            if (id == gOwner || llListFindList(gAllowed, [ av_name ]) != -1) {
                teleport(id);
            } else {
                llWhisper(0, llKey2Name(id) + " not permitted.");
            }
        }
    }
}

